/*
 * @Author: Wangjun
 * @Date: 2021-06-08 11:35:05
 * @LastEditTime: 2022-10-28 14:52:56
 * @LastEditors: Wangjun
 * @Description:查询schema
 * @FilePath: \xrengined:\go\src\gitee.com\haodreams\query\schema.go
 * hnxr
 */
package query

import (
	"errors"
	"fmt"
	"log"
	"runtime/debug"
	"strings"
	"time"

	"gitee.com/haodreams/golib/logs"
	"github.com/alecthomas/participle/v2"
)

type Schema struct {
	Table       string  `parser:"'FROM' '(' @String ')'"`
	Funcs       []*Func `parser:"@@+"`
	Err         string  //错误消息
	initError   error   //初始化的错误
	Result      string  //暂存数据的地方
	Desc        string  //存放描述的地方
	Query       string
	SourceQuery string //优化后的查询
	LastRunTime int64  //最后一次运行时间
	UsedTime    string //耗时
	Value       string //运行结果
	OK          bool
	q           *Table
}

/**
 * @description:获取快速表
 * @param {*}
 * @return {*}
 */
func (m *Schema) FastTable() (table *Table, err error) {
	if !m.OK {
		return
	}
	defer func() {
		if e := recover(); e != nil {
			m.Err = fmt.Sprint(e)
			err = errors.New(m.Err)
			m.OK = false
			log.Println(e, string(debug.Stack()))
			return
		}
	}()
	now := time.Now()
	m.LastRunTime = now.Unix()
	n := len(m.Funcs)
	q := m.q
	for i := 0; i < n; i++ {
		f := m.Funcs[i]
		q, err = f.f(q, f.Params)
		if err != nil {
			m.OK = false
			break
		}
	}

	if err != nil {
		m.Err = err.Error()
	} else {
		m.Err = ""
	}
	table = q
	return
}

/**
 * @description: 获取快速表(大表转小标)
 * @param {string} s
 * @return {*}
 */
func NewFastTable(s string) (table *Table, err error) {
	schema, err := NewSchema(s)
	if err != nil {
		return
	}
	table, err = schema.FastTable()
	return
}

/**
 * @description: 开启优化查询，后面需要手动调用 Parser
 * @param {string} s
 * @return {*}
 */
func NewFastSchema(s string) (schema *Schema, err error) {
	schema = new(Schema)
	ss := strings.Split(s, ").")
	if len(ss) < 3 {
		err = schema.Parser(s)
		if err != nil {
			logs.Error(err)
		}
		return
	}
	schema.SourceQuery = s
	pos := 0
	for i, s := range ss {
		if i == 0 {
			continue
		}
		if strings.Contains(s, ".") {
			pos = i
			break
		}
	}
	if pos > 1 {
		key := strings.Join(ss[:pos], ").") + ")"
		tableName := strings.ReplaceAll(key, "\"", "'")
		tableName = strings.TrimSuffix(tableName, ".")
		table := GetTable(tableName)
		if table == nil {
			table, err = NewFastTable(key)
			if err != nil {
				schema.Err = err.Error()
				schema.initError = err
				//logs.Error(err)
				return schema, err
			}
			err = RegisterTable(tableName, table)
			if err != nil {
				schema.Err = err.Error()
				logs.Error(err.Error())
				return
			}
		}
		query := strings.Replace(schema.SourceQuery, key, "FROM(\""+tableName+"\")", 1)
		//log.Println(query)
		err = schema.Parser(query)
	} else {
		err = schema.Parser(schema.SourceQuery)
	}
	if err != nil {
		logs.Error(err)
		schema.initError = err
	}
	return
}

/**
 * @description: 不开启优化
 * @param {string} s
 * @return {*}
 */
func NewSchema(s string) (se *Schema, err error) {
	se = new(Schema)
	err = se.Parser(s)
	return
}

// GetColumns 获取列名
func (m *Schema) GetColumns() []string {
	return m.q.Keys()
}

// 初始化数据
func (m *Schema) Parser(s string) (err error) {
	parse, err := participle.Build[Schema]()
	if err != nil {
		m.Err = err.Error()
		return
	}

	t, err := parse.ParseString("", s)
	if err != nil {
		m.Err = err.Error()
		return
	}
	m.Query = s

	m.Funcs = t.Funcs
	m.Table = t.Table

	//整理数据
	m.Table = strings.TrimPrefix(m.Table, "\"")
	m.Table = strings.TrimSuffix(m.Table, "\"")
	for _, f := range m.Funcs {
		for _, p := range f.Params {
			if p.String != nil {
				*p.String = strings.TrimPrefix(*p.String, "\"")
				*p.String = strings.TrimSuffix(*p.String, "\"")
			}
		}
	}

	//初始化数据表
	m.q = GetTable(m.Table)
	if m.q == nil {
		err = fmt.Errorf("'%s' 数据表没有注册", m.Table)
		m.Err = err.Error()
		return
	}
	//初始化每个方法
	for _, f := range m.Funcs {
		if f == nil {
			err = fmt.Errorf("'%s' 无效的函数调用", m.Table)
			m.Err = err.Error()
			return
		}
		if fn, ok := mq.mapFunc[f.Name]; ok {
			f.f = fn
		} else {
			err = fmt.Errorf("'%s' 没有此函数 [%s]", m.Table, f.Name)
			m.Err = err.Error()
			return
		}

		if f.f == nil {
			err = fmt.Errorf("'%s.%s' 无效的函数调用", m.Table, f.Name)
			m.Err = err.Error()
			return
		}
		if strings.HasPrefix(f.Name, "If") {
			if len(f.Params) != 3 {
				err = fmt.Errorf("'%s.%s' 参数个数错误", m.Table, f.Name)
				m.Err = err.Error()
				return
			}
		}
	}

	m.OK = true
	return nil
}

// Eval 执行计算
func (m *Schema) Eval() (value interface{}, err error) {
	if m.initError != nil {
		return nil, m.initError
	}
	if !m.OK {
		return
	}
	defer func() {
		if e := recover(); e != nil {
			m.Err = fmt.Sprint(e)
			err = errors.New(m.Err)
			m.OK = false
			log.Println(e, string(debug.Stack()))
			return
		}
	}()
	now := time.Now()
	m.LastRunTime = now.Unix()
	n := len(m.Funcs)
	q := m.q
	for i := 0; i < n; i++ {
		f := m.Funcs[i]
		q, err = f.f(q, f.Params)
		if err != nil {
			break
		}
	}

	if err != nil {
		m.Err = err.Error()
	} else {
		m.Err = ""
	}
	m.UsedTime = time.Since(now).String()
	value = q.Result()
	switch v := value.(type) {
	case float64:
		m.Value = fmt.Sprintf("%.4f", v)
	case float32:
		m.Value = fmt.Sprintf("%.4f", v)
	default:
		m.Value = fmt.Sprint(value)
	}
	return
}

// 最后一个缺省是and运算
type QueryFunc func(*Table, []*Param) (*Table, error)

//type FuncStatic func(*Query, string) (interface{}, error)

// FORM("es").LT("Genpower.Value",0).GT("Genpower.Value",-5000).Sum("Genpower.Value")
type Func struct {
	Name   string   `parser:"'.' @Ident"`
	Params []*Param `parser:"'(' ( @@ ( ',' @@ )* )? ')'"`

	f QueryFunc
	//static FuncStatic `json:"-"`
}

func (m *Func) String() string {
	s := "func:" + m.Name + " "
	for _, p := range m.Params {
		if p == nil {
			continue
		}
		s += fmt.Sprint(",", p.GetValue())
	}
	return s
}

func (m *Func) Key() string {
	return *m.Params[0].String
}

type Param struct {
	Number *float64 `parser:"@('-'?(Float|Int))"`
	String *string  `parser:"| @String"`
}

func (m *Param) Key() string {
	if m.String == nil {
		return ""
	}
	return *m.String
}
func (m *Param) GetValue() interface{} {
	if m.Number != nil {
		return *m.Number
	}
	if m.String != nil {
		return *m.String
	}
	return nil
}

func (m *Param) Float64() float64 {
	if m.Number == nil {
		return 0
	}
	return *m.Number
}
